//------------------------------------------------------------------------------
//  <copyright file="RegisterExchange.h" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//  </copyright>
//------------------------------------------------------------------------------	

#pragma once

#include "stdafx.h"
#include "ControllerStateMachineRegisters.h"
#include "RegisterContainer.h"
#include <iostream>

/// <summary>
/// Class used to exchange registers between two threads.  Assumes that Publish() will
/// only be called by a single thread and Consume() will also only be called by a single
/// thread (but typically not the same thread that calls Publish()).
/// </summary>
/// Type <c>T</c> indicates the type of a regiser value.
template <class T> class RegisterExchange
{
public:
	/// <summary> Initializes a new instance of the RegisterExchange class. </summary>
	/// <param name="registerCount"> The number of registers the exchange buffer will hold. </param>
	/// <param name="pRegisterSizesInBytes"> The size of each register the exchange buffer will hold. </param>
	RegisterExchange(unsigned int registerCount, unsigned int pRegisterSizesInBytes[]);

	/// <summary> dtor </summary>
	~RegisterExchange();

	/// <summary> 
	/// Publishes the values pointed pRegistersToPublish to the exchange buffer.
	/// </summary>
	/// <param name="pRegistersToPublish"> The registers to publish. </param>
	void Publish(ControllerStateMachineRegisters<T> *pRegistersToPublish);

	/// <summary> 
	/// Checks if there is new data in the exchange buffer, and if there is new data
	/// copies it to the registers pointed to by pDestinationRegisters.
	/// </summary>
	/// <param name="pDestinationRegisters">
	/// Desitnation for to copy registers to, if there are new values in the exchange buffer.
	/// Values are untouched if there is no new data.
	/// </param>
	/// <returns>
	/// True if new data was consumed and the contents of pDestinationRegister
	/// were changed.  False otherwise.
	/// </returns>
	bool Consume(ControllerStateMachineRegisters<T> *pDestinationRegisters);

	/// <summary> Gets the number of registers in the exchange buffer. </summary>
	/// <returns> The number of registers in the exchange buffer. </returns>
	unsigned int GetRegisterCount();

	/// <summary> Gets the size in bytes of the indicated register. </summary>
	/// <param name="registerNumber"> Indicates which register to retrieve the size of. </param>
	/// <returns> The size in bytes of the indicated register. </returns>
	unsigned int GetRegisterSizeInBytes(unsigned int registerNumber);
private:
	/// <summary> The number of registers in the exchange buffer. </summary>
	unsigned int registerCount;

	/// <summary> Sizes of the registers in the exchange buffer. </summary>
	unsigned int *pRegisterSizesInBytes;

	/// <summary> The current publish buffer. </summary>
	RegisterContainer<T> *pPublishBuffer;

	/// <summary> The current consume buffer. </summary>
	RegisterContainer<T> *pConsumeBuffer;

	/// <summary> The current exchange buffer. </summary>
	RegisterContainer<T> *pExchangeBuffer;
};

/// <summary> Initializes a new instance of the RegisterExchange class. </summary>
/// <param name="registerCount"> The number of registers the exchange buffer will hold. </param>
/// <param name="pRegisterSizesInBytes"> The size of each register the exchange buffer will hold. </param>
template <class T>
RegisterExchange<T>::RegisterExchange(unsigned int registerCount, unsigned int pRegisterSizesInBytes[])
{
	this->registerCount = registerCount;

	this->pRegisterSizesInBytes = new unsigned int[registerCount];

	for(unsigned int i = 0; i < this->registerCount; ++i)
	{
		this->pRegisterSizesInBytes[i] = pRegisterSizesInBytes[i];
	}
	

	this->pPublishBuffer = new RegisterContainer<T>();
	this->pPublishBuffer->pRegisters = new ControllerStateMachineRegisters<T>(
		this->registerCount,
		this->pRegisterSizesInBytes);
	this->pPublishBuffer->IsNew = false;

	this->pConsumeBuffer = new RegisterContainer<T>();
	this->pConsumeBuffer->pRegisters = new ControllerStateMachineRegisters<T>(
		this->registerCount,
		this->pRegisterSizesInBytes);
	this->pConsumeBuffer->IsNew = false;

	this->pExchangeBuffer = new RegisterContainer<T>();
	this->pExchangeBuffer->pRegisters = new ControllerStateMachineRegisters<T>(
		this->registerCount,
		this->pRegisterSizesInBytes);
	this->pExchangeBuffer->IsNew = false;
}

/// <summary> dtor </summary>
template <class T>
RegisterExchange<T>::~RegisterExchange()
{
	delete this->pRegisterSizesInBytes;
	delete this->pConsumeBuffer;
	delete this->pExchangeBuffer;
	delete this->pPublishBuffer;
}

/// <summary> 
/// Publishes the values pointed pRegistersToPublish to the exchange buffer.
/// </summary>
/// <param name="pRegistersToPublish"> The registers to publish. </param>
template <class T>
void RegisterExchange<T>::Publish(ControllerStateMachineRegisters<T> *pRegistersToPublish)
{
	ControllerStateMachineRegisters<T>::CopyRegisters(pRegistersToPublish, this->pPublishBuffer->pRegisters);
	this->pPublishBuffer->IsNew = true;

	this->pPublishBuffer = (RegisterContainer<T> *)InterlockedExchangePointer(
		(volatile PVOID *)&(this->pExchangeBuffer),
		this->pPublishBuffer);
}

/// <summary> 
/// Checks if there is new data in the exchange buffer, and if there is new data
/// copies it to the registers pointed to by pDestinationRegisters.
/// </summary>
/// <param name="pDestinationRegisters">
/// Desitnation for to copy registers to, if there are new values in the exchange buffer.
/// Values are untouched if there is no new data.
/// </param>
/// <returns>
/// True if new data was cosumed and the contents of pDestinationRegister
/// were changed.  False otherwise.
/// </returns>
template <class T>
bool RegisterExchange<T>::Consume(ControllerStateMachineRegisters<T> *pDestinationRegisters)
{
	this->pConsumeBuffer->IsNew = false;
	this->pConsumeBuffer = (RegisterContainer<T> *)InterlockedExchangePointer(
		(volatile PVOID *)&(this->pExchangeBuffer),
		this->pConsumeBuffer);
	if (this->pConsumeBuffer->IsNew)
	{
		ControllerStateMachineRegisters<T>::CopyRegisters(this->pConsumeBuffer->pRegisters, pDestinationRegisters);
		this->pConsumeBuffer->IsNew = false;
		return true;
	}

	return false;
}

/// <summary> Gets the number of registers in the exchange buffer. </summary>
/// <returns> The number of registers in the exchange buffer. </returns>
template <class T>
unsigned int RegisterExchange<T>::GetRegisterCount()
{
	return this->registerCount;
}

/// <summary> Gets the size in bytes of the indicated register. </summary>
/// <param name="registerNumber"> Indicates which register to retrieve the size of. </param>
/// <returns> The size in bytes of the indicated register. </returns>
template <class T>
unsigned int RegisterExchange<T>::GetRegisterSizeInBytes(unsigned int registerNumber)
{
	if (registerNumber >= this->registerCount)
	{
		throw "Invalid register number";
	}

	return this->pRegisterSizesInBytes(registerNumber);
}
